using System;
using System.Reflection;
using System.ComponentModel;
using System.Threading;

/**
 * Taken from the original code by Mark Brimble 
 */
namespace ConfigManager.Tests
{
	#region Reflection Class ----------------------------------------------
    /// <summary>
    /// Static Reflection helper class
    /// </summary>
	public sealed class Reflection
    {
		#region Private Constructor -------------------------------------------
		private Reflection()
		{
		}
		#endregion //Private Constructor 
		
		#region Public Methods ------------------------------------------------
        /// <summary>
        /// Gets the non public field from the instance
        /// </summary>
        /// <param name="FieldName"> the name of the private field</param>
        /// <param name="instance">the object containing the field</param>
        /// <returns>field as the object</returns>
        public static object GetConst(string FieldName, object instance)
        {
            Type type = instance.GetType();
            return type.GetField(FieldName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(instance);
        }
        
        /// <summary>
        /// Gets the non public field from the instance
        /// </summary>
        /// <param name="FieldName"> the name of the private field</param>
        /// <param name="instance">the object containing the field</param>
        /// <returns>field as the object</returns>
        public static object GetField(string FieldName, object instance)
        {
            Type type = instance.GetType();
            return type.GetField(FieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).GetValue(instance);
        }

        /// <summary>
        /// Gets the non public field of a specific type from the instance
        /// </summary>
        /// <param name="FieldName"> the name of the private field</param>
        /// <param name="instance">the object containing the field</param>
        /// <param name="type">the type of the field eg: ImageList</param>
        /// <returns>field as the object</returns>
        /// <remarks>This is good for any container/component controls like image list that are not seen in the design of the UI of the class</remarks> 
        public static object GetField(string FieldName, object instance, Type type)
        {
            return type.GetField(FieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public).GetValue(instance);
        }

        /// <summary>
        /// Sets the non public field from the instance
        /// </summary>
        /// <param name="FieldName"> the name of the private field</param>
        /// <param name="instance">the object containing the field</param>
        /// <param name="value">the new value for the field</param>
        /// <returns>field as the object</returns>
        public static void SetField(string FieldName, object instance, object value)
        {
            Type type = instance.GetType();
            type.GetField(FieldName, BindingFlags.Instance | BindingFlags.NonPublic).SetValue(instance, value);
        }

        /// <summary>
        /// Sets the non public field of a specific type from the instance
        /// </summary>
        /// <param name="FieldName"> the name of the private field</param>
        /// <param name="instance">the object containing the field</param>
        /// <param name="type">the type of the field eg: ImageList</param>
        /// <param name="value">the new value for the field</param>
        /// <returns>field as the object</returns>
        /// <remarks>This is good for any container/component controls like image list that are not seen in the design of the UI of the class</remarks> 
        public static void SetField(string FieldName, object instance, Type type, object value)
        {
            type.GetField(FieldName, BindingFlags.Instance | BindingFlags.NonPublic).SetValue(instance, value);
        }
		
		/// <summary>
        /// Invokes the given method for a private, or public object
        /// </summary>
        /// <param name="methodName">name of the method to be invoked</param>
        /// <param name="instance">the object containing the field</param>
        /// <param name="args">any arguments to be passed to the method call</param>
        /// <returns>any response from the method as an object</returns>
        /// <exception cref="ArgumentException">Method name is not found for the instance</exception>
        public static object InvokeMethod(string methodName, object instance, object[] args)
        {
			try
            {
                return instance.GetType().InvokeMember(methodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.InvokeMethod, Type.DefaultBinder, instance, args, Thread.CurrentThread.CurrentCulture);
            }
            catch(Exception e)
            {
				if(e.InnerException != null)
				{
					throw e.InnerException;
				}
				else
				{
                    throw new ArgumentException("InvokeMethod in Common.Reflection failed.");
				}
			}
        }

		/// <summary>
		/// Invokes the given method for a private, or public object
		/// </summary>
		/// <param name="methodName">name of the method to be invoked</param>
		/// <param name="instance">the object containing the field</param>
		/// <param name="args">any arguments to be passed to the method call</param>
		/// <param name="type">the type of the object instance</param>
		/// <returns>any response from the method as an object</returns>
		/// <exception cref="ArgumentException">Method name is not found for the instance</exception>
		public static object InvokeMethod(string methodName, object instance, object[] args, Type type)
		{
			try
			{
				return type.InvokeMember(methodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.InvokeMethod, Type.DefaultBinder, instance, args, Thread.CurrentThread.CurrentCulture);
			}
			catch(Exception e)
			{
				if(e.InnerException != null)
				{
					throw e.InnerException;
				}
				else
				{
                    throw new ArgumentException("InvokeMethod in Common.Reflection failed.");
				}
			}
		}


		/// <summary>
		/// Reads a property value by invoking the internal get_ method in a class instance.
		/// </summary>
		/// <param name="propertyName">The property name to read.</param>
		/// <param name="instance">The instance of the object with the property you wish to read.</param>
		/// <returns>The value of the property. The caller will need to typecast this to the correct type on return.</returns>
		public static object ReadProperty(string propertyName, object instance)
		{
			return InvokeMethod("get_" + propertyName, instance, null);
		}
		
		/// <summary>
		/// Reads a property value by invoking the internal get_ method in a class instance.
		/// </summary>
		/// <param name="propertyName">The property name to read.</param>
		/// <param name="instance">The instance of the object with the property you wish to read.</param>
		/// <param name="type">The type of the object</param>
		/// <returns>The value of the property. The caller will need to typecast this to the correct type on return.</returns>
		public static object ReadProperty(string propertyName, object instance, Type type)
		{
			return InvokeMethod("get_" + propertyName, instance, null, type);
		}

		/// <summary>
		/// Writes a value to a property by invoking the set_ method in a class instance
		/// </summary>
		/// <param name="propertyName">The property name to weite.</param>
		/// <param name="instance">The instance of the object with the property you wish to write.</param>
		/// <param name="value">The value to set this property to.</param>
		public static void WriteProperty(string propertyName, object instance, object value)
		{
			InvokeMethod("set_" + propertyName, instance, new object[] {value});
		}

		/// <summary>
		/// Retrieves the value of a NonPublic or Public static field
		/// </summary>
		/// <param name="fieldName">Name of the static field</param>
		/// <param name="type">The type of the field eg: typeof(EventManager)</param>
		/// <returns>Value of the field</returns>
		public static object GetStaticField(string fieldName, Type type)
		{
            return type.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Public).GetValue(null);
		}

		/// <summary>
		/// Sets the value of a NonPublic or Public static field
		/// </summary>
		/// <param name="fieldName">Name of the static field</param>
		/// <param name="type">The type of the field eg: typeof(EventManager)</param>
		/// <param name="value">The value to set the field too</param>
		public static void SetStaticField(string fieldName, Type type, object value)
		{
			type.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).SetValue(null, value);
		}
		
		/// <summary>
		/// Invokes the given static method for a private, or public type.
		/// </summary>
		/// <param name="methodName">
		/// The name of the method to be invoked.
		/// </param>
		/// <param name="type">
		/// The type of the class that contains the method.
		/// </param>
		/// <param name="args">
		/// Any arguments to be passed to the method call.
		/// </param>
		/// <returns>
		/// Any response from the method as an object.
		/// </returns>
		/// <exception cref="ArgumentException">Method name is not found for the instance</exception>
		public static object InvokeStaticMethod(string methodName, Type type, object[] args)
		{
			try
			{
				return type.InvokeMember(methodName, BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.InvokeMethod, Type.DefaultBinder, null, args, Thread.CurrentThread.CurrentCulture);
			}
			catch(Exception e)
			{
				if(e.InnerException != null)
				{
					throw e.InnerException;
				}
				else
				{
                    throw new ArgumentException("InvokeMethod in Common.Reflection failed.");
				}
			}
		}


		/// <summary>
		/// Returns the specifiede attribute decorating an objects Property
		/// </summary>
		/// <param name="propertyName">name of the property that has attributes attached.</param>
		/// <param name="type">The type of the object containing the property required.</param>
		/// <param name="attribute">The type of attribute to search for.</param>
		/// <returns></returns>
		public static object[] GetPropertyAttributes(string propertyName, Type type, Type attribute)
		{
			try
			{ 
				PropertyInfo propertyInfo = type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
				return propertyInfo != null ? propertyInfo.GetCustomAttributes(attribute, false) : null;
			}
			catch(Exception e)
			{
				if(e.InnerException != null)
				{
					throw e.InnerException;
				}
				else
				{
                    throw new ArgumentException("GetPropertyAttributes in Common.Reflection failed.");
				}
			}
		}

		/// <summary>
		/// Returns all the attributes decorating an objects Property
		/// </summary>
		/// <param name="propertyName">name of the property that has attributes attached.</param>
		/// <param name="type">The type of the object containing the property required.</param>
		/// <returns></returns>
		public static object[] GetPropertyAttributes(string propertyName, Type type)
		{
			try
			{ 
				PropertyInfo propertyInfo = type.GetProperty(propertyName,BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
				return propertyInfo != null ? propertyInfo.GetCustomAttributes(false) : null;
			}
			catch(Exception e)
			{
				if(e.InnerException != null)
				{
					throw e.InnerException;
				}
				else
				{
                    throw new ArgumentException("GetPropertyAttributes in Common.Reflection failed.");
				}
			}
		}
		/// <summary>
		/// Returns the specifiede attribute decorating an objects field
		/// </summary>
		/// <param name="fieldName">name of the field that has attributes attached.</param>
		/// <param name="type">The type of the object containing the field required.</param>
		/// <param name="attribute">The type of attribute to search for.</param>
		/// <returns></returns>
		public static object[] GetFieldAttributes(string fieldName, Type type, Type attribute)
		{
			try
			{ 
				FieldInfo fieldInfo = type.GetField(fieldName,  BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
				return fieldInfo != null ? fieldInfo.GetCustomAttributes(attribute, false) : null;
			}
			catch(Exception e)
			{
				if(e.InnerException != null)
				{
					throw e.InnerException;
				}
				else
				{
					throw new ArgumentException("GetFieldAttributes in Common.Reflection failed.");
				}
			}
		}


		/// <summary>
		/// Returns the specifiede attribute decorating an objects field
		/// </summary>
		/// <param name="fieldName">name of the field that has attributes attached.</param>
		/// <param name="type">The type of the object containing the field required.</param>
		/// <returns></returns>
		public static object[] GetFieldAttributes(string fieldName, Type type)
		{
			try
			{ 
				FieldInfo fieldInfo = type.GetField(fieldName,  BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
				return fieldInfo != null ? fieldInfo.GetCustomAttributes(false) : null;
			}
			catch(Exception e)
			{
				if(e.InnerException != null)
				{
					throw e.InnerException;
				}
				else
				{
					throw new ArgumentException("GetFieldAttributes in Common.Reflection failed.");
				}
			}
		}

        /// <summary>
        /// Returns a colloection of attributes decorating a method
        /// </summary>
        /// <param name="fieldName">name of the method that has attributes attached.</param>
        /// <param name="type">The type of the object containing the method required.</param>
        /// <returns></returns>
        public static object[] GetMethodAttributes(string methodName, Type type)
        {
            try
            {
                MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
                return methodInfo != null ? methodInfo.GetCustomAttributes(false) : null;
            }
            catch (Exception e)
            {
                if (e.InnerException != null)
                {
                    throw e.InnerException;
                }
                else
                {
                    throw new ArgumentException("GetMethodAttributes in Common.Reflection failed.");
                }
            }
        }

        /// <summary>
        /// Returns a colloection of the specified attributes decorating a method
        /// </summary>
        /// <param name="fieldName">name of the method that has attributes attached.</param>
        /// <param name="type">The type of the object containing the method required.</param>
		/// <param name="attribute">The type of attribute to search for.</param>
        /// <returns></returns>
        public static object[] GetMethodAttributes(string methodName, Type type, Type attribute)
        {
            try
            {
                MethodInfo methodInfo = type.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
                return methodInfo != null ? methodInfo.GetCustomAttributes(attribute, false) : null;
            }
            catch (Exception e)
            {
                if (e.InnerException != null)
                {
                    throw e.InnerException;
                }
                else
                {
                    throw new ArgumentException("GetMethodAttributes in Common.Reflection failed.");
                }
            }
        }
       
        /// <summary>
		/// Returns true, if the property has a 'get'er.
		/// </summary>
		/// <param name="propertyName">name of the property that is of interest.</param>
		/// <param name="type">The type of the object containing the property required.</param>
		/// <returns></returns>
		public static bool PropertyHasGetter(string propertyName, Type type)
		{
			try
			{ 
				PropertyInfo propertyInfo = type.GetProperty(propertyName);
				return propertyInfo.CanRead;
			}
			catch(Exception e)
			{
				if(e.InnerException != null)
				{
					throw e.InnerException;
				}
				else
				{
                    throw new ArgumentException("PropertyHasGetter in Common.Reflection failed.");
				}
			}
		}

		/// <summary>
		/// Returns true, if the property has a 'set'er.
		/// </summary>
		/// <param name="propertyName">name of the property that is of interest.</param>
		/// <param name="type">The type of the object containing the property required.</param>
		/// <returns></returns>
		public static bool PropertyHasSetter(string propertyName, Type type)
		{
			try
			{ 
				PropertyInfo propertyInfo = type.GetProperty(propertyName);
				return propertyInfo.CanWrite;
			}
			catch(Exception e)
			{
				if(e.InnerException != null)
				{
					throw e.InnerException;
				}
				else
				{
                    throw new ArgumentException("PropertyHasSetter in Common.Reflection failed.");
				}
			}
		}

		/// <summary>
		/// Gets a private type from a class
		/// </summary>
		/// <param name="parentType">The parent type to search for the private type</param>
		/// <param name="privateTypeName">The name of the private type</param>
		/// <returns>The private type or null if not found</returns>
		public static Type GetPrivateType(Type parentType, string privateTypeName)
		{
			Type[] types = parentType.GetNestedTypes(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
			foreach( Type type in types )
			{
				if( type.Name == privateTypeName )
					return type;
			}
			return null;
		}

		/// <summary>
		/// Gets a private type from a class
		/// </summary>
		/// <param name="parentType">The parent type to search for the private type. E.g. typeof(Infomedia.Common.Reflection)</param>
		/// <param name="privateEnumTypeName">The name of the private enum within the parent type. E.g. "SearchTypes".</param>
		/// <param name="privateEnumValueName">The name of the enum value within the private enum type E.g. "SearchByPartNumber".</param>
		/// <returns>The Enum value of the private enum type.</returns>
		public static Enum GetPrivateEnumValue(Type parentType, string privateEnumTypeName, string privateEnumValueName)
		{
			Type enumType = GetPrivateType(parentType, privateEnumTypeName);
			object result = Reflection.GetStaticField(privateEnumValueName, enumType);

			return result as Enum;
		}

		#endregion //Public Methods 
	}
		#endregion //Reflection 
}

